{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "view-in-github"
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/vitaldb/examples/blob/master/mbp_mins.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Fvipu9X94deV"
   },
   "source": [
    "# Calculation of MINS risk depending on BP during surgery\n",
    "In this example, we will calculate the risk of MINS(myocardial injury after non-cardiac surgery, MINS) depending on blood pressure during surgery.\n",
    "\n",
    "> Note that <b>all users who use Vital DB, an open biosignal dataset, must agree to the Data Use Agreement below. \n",
    "</b> If you do not agree, please close this window. \n",
    "Click here: [Data Use Agreement](https://vitaldb.net/dataset/?query=overview&documentId=13qqajnNZzkN7NZ9aXnaQ-47NWy7kx-a6gbrcEsi-gak&sectionId=h.vcpgs1yemdb5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "SqawrQV74y0G"
   },
   "source": [
    "## Required libraries and datasets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "id": "p4HnPH-U4zlv"
   },
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "df_cases = pd.read_csv(\"https://api.vitaldb.net/cases\")  # 임상 정보\n",
    "df_trks = pd.read_csv('https://api.vitaldb.net/trks')  # 트랙 목록\n",
    "df_labs = pd.read_csv('https://api.vitaldb.net/labs')  # lab 데이터"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "SmpjUVoJ48g3"
   },
   "source": [
    "## Case Selection\n",
    "Use only 100 patients with troponin I results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 35
    },
    "id": "bC5fzyn_5FnU",
    "outputId": "c33c9e02-6ddc-4132-b186-3fe793178d51"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total 100 cases found\n"
     ]
    }
   ],
   "source": [
    "caseids = list(\n",
    "    set(df_trks[df_trks['tname'] == 'Solar8000/ART_MBP']['caseid']) & \n",
    "    set(df_labs[df_labs['name'] == 'Troponin I']['caseid'])\n",
    ")\n",
    "caseids = caseids[:100]\n",
    "print('Total {} cases found'.format(len(caseids)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "i71VaVkr5HDq"
   },
   "source": [
    "## Load and calculate data for each case\n",
    "- First of all, calculate the postoperative troponin I concentration for each case from vitaldb, and determine whether MINS occurs from this. In this example, MINS is defined as troponin I > 0.028 ng/mL.\n",
    "- Also, let's get the ART_MBP data and find the ratio of the measured values that stayed below the threshold during the entire operation. The threshold range is 40-80mmHg."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 575
    },
    "id": "mRz6-gA65WPd",
    "outputId": "69eb4e02-cba6-42c0-a431-18e538f2e6df"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loading 1...0.01, \n",
      "loading 4...0.01, \n",
      "loading 6152...0.1, MINS\n",
      "loading 10...0.69, MINS\n",
      "loading 2058...0.01, \n",
      "loading 12...0.07, MINS\n",
      "loading 2060...0.01, \n",
      "loading 4109...no postop troponin I\n",
      "loading 2063...0.02, \n",
      "loading 2064...0.01, \n",
      "loading 17...0.01, \n",
      "loading 6156...0.01, \n",
      "loading 19...no postop troponin I\n",
      "loading 20...0.02, \n",
      "loading 4115...0.03, MINS\n",
      "loading 6159...no postop troponin I\n",
      "loading 6166...0.01, \n",
      "loading 25...0.02, \n",
      "loading 6174...0.01, \n",
      "loading 4128...0.01, \n",
      "loading 2082...0.01, \n",
      "loading 6180...0.01, \n",
      "loading 2085...no postop troponin I\n",
      "loading 4135...0.02, \n",
      "loading 6185...no postop troponin I\n",
      "loading 6186...0.01, \n",
      "loading 4143...0.01, \n",
      "loading 2097...no postop troponin I\n",
      "loading 4146...0.01, \n",
      "loading 2100...0.01, \n",
      "loading 4148..."
     ]
    }
   ],
   "source": [
    "# Set blood pressure threshold\n",
    "mbp_thresholds = np.arange(40, 80)\n",
    "\n",
    "# Save the final result\n",
    "df = pd.DataFrame()\n",
    "for caseid in caseids:\n",
    "    print('loading {}...'.format(caseid), flush=True, end='')\n",
    "\n",
    "    # Column ['anend'] : anesthesia end time\n",
    "    aneend = df_cases[(df_cases['caseid'] == caseid)]['aneend'].values[0]\n",
    "\n",
    "    # Maximum creatinine concentration within 48 hours after surgery\n",
    "    postop_tpi = df_labs[(df_labs['caseid'] == caseid) & (df_labs['dt'] > aneend) &\n",
    "        (df_labs['dt'] < aneend + 48 * 3600) & (df_labs['name'] == 'Troponin I')]['result'].max(skipna=True)\n",
    "    if not postop_tpi or np.isnan(postop_tpi):\n",
    "        print('no postop troponin I')\n",
    "        continue\n",
    "\n",
    "    # mins = postop_tpi > 0.028\n",
    "    mins = postop_tpi > 0.028\n",
    "\n",
    "    # Blood pressure during surgery\n",
    "    tid_mbp = df_trks[(df_trks['caseid'] == caseid) & (df_trks['tname'] == 'Solar8000/ART_MBP')]['tid'].values[0]\n",
    "    mbps = pd.read_csv('https://api.vitaldb.net/' + tid_mbp).values[:,1]\n",
    "    mbps = mbps[~np.isnan(mbps)]\n",
    "    mbps = mbps[(mbps > 20) & (mbps < 150)]\n",
    "    if len(mbps) < 10:\n",
    "        print('no mbp')\n",
    "        continue\n",
    "\n",
    "    # Calculate the percentage that stays for the time as increasing the blood pressure by 1 unit.\n",
    "    row = {'mins':mins}\n",
    "    for mbp_threshold in mbp_thresholds:\n",
    "        row['under{}'.format(mbp_threshold)] = np.nanmean(mbps < mbp_threshold) * 100\n",
    "\n",
    "    # Append the result into row\n",
    "    df = df.append(row, ignore_index=True)\n",
    "\n",
    "    print('{}, {}'.format(postop_tpi, 'MINS' if mins else ''))\n",
    "\n",
    "print('{} MINS {:.1f}%'.format(df['mins'].sum(), df['mins'].mean() * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "NCz7SN-N5ajv"
   },
   "source": [
    "## Calculate the odds ratio for MINS prediction depending on BP threshold\n",
    "\n",
    "Let's calculate the odds ratio regarding how much each BP case increases the risk of MINS, using the above-measured values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "kqldpT3h5g9X"
   },
   "outputs": [],
   "source": [
    "# Get odd ration using univariate logistic regression\n",
    "import statsmodels.api as sm\n",
    "df['intercept'] = 1\n",
    "df['mins'] = df['mins'].astype(bool)\n",
    "odd_ratios = []\n",
    "for mbp_threshold in mbp_thresholds:\n",
    "    c = 'under{}'.format(mbp_threshold)\n",
    "    model = sm.Logit(df['mins'], df[['intercept', c]])\n",
    "    res = model.fit()\n",
    "    b = res.params[c]\n",
    "    pval = res.pvalues[c]\n",
    "    odd_ratios.append(np.exp(b))\n",
    "    print('{}\\tb={:.3f}, exp(b)={:.3f}, pval={:.3f}'.format(c, b, np.exp(b), pval))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Wi1dQ0yE5j_V"
   },
   "source": [
    "## Draw a graph\n",
    "We can find out that the odds ratio of MINS increases as the time that MBP stays below increases."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "gnTsUGHE5rcY"
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "plt.plot(mbp_thresholds, odd_ratios)\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "authorship_tag": "ABX9TyO+1QhCjcQNn2uiHLApIK9V",
   "include_colab_link": true,
   "name": "mbp_mins.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
